﻿using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;

namespace TPXSDK.NET.Mission;

/// <summary>
/// Fly命令编译器
/// </summary>
public class FlyCompiler
{
    #region 成员变量
    public delegate void CmpHandler(CmpEventArgs e);

    /// <summary>
    /// 编译事件
    /// </summary>
    public event CmpHandler? CmpEvent;

    /// <summary>
    /// 任务指令列表
    /// </summary>
    public List<FlyCmdItem> CmdList { get; set; } = [];

    /// <summary>
    /// 词元集合列表
    /// </summary>
    private readonly List<Token> _tokenList = [];

    /// <summary>
    /// 任务文件名称
    /// </summary>
    private string _flyFile = string.Empty;

    private int _lineNum;
    private readonly StringBuilder _sb2Linelex = new();

    /// <summary>
    /// 当前词元索引
    /// </summary>
    private int _tokenIndex;

    /// <summary>
    /// 航点指令是否使用快速航点模式
    /// </summary>
    public bool FastWpMode { get; set; }

    /// <summary>
    /// 导航指令是否使用绝对高度
    /// </summary>
    public bool AmslMode { get; set; }

    /// <summary>
    /// 词元管理器
    /// </summary>
    internal TokenizerAdmin? TokenAdmin { get; private set; }

    /// <summary>
    /// 编译过程错误信息
    /// </summary>
    public string? ErrorMsg { get; set; }

    /// <summary>
    /// 汇报通知信息
    /// </summary>
    public string? ReportMsg { get; set; }
    #endregion

    public FlyCompiler()
    {
        TokenAdmin = new TokenizerAdmin(this);
    }

    /// <summary>
    /// 编译Fly文件(*.fly), 生成指令列表
    /// </summary>
    public bool Compile(string flyFile)
    {
        if (!File.Exists(flyFile))
        {
            Report($"{flyFile} not exists.", CmpEventArgs.MsgType.Error, -1);
            return false;
        }

        // 清理相关数据，准备编译新文件
        Clean();
        _flyFile = flyFile;
        using var sr = new StreamReader(flyFile);
        var result = LexAnalyze(sr);
        if (result) result = SyntaxAnalyze();
        if (result) result = CodeGen();
        return result;
    }

    /// <summary>
    /// 清理编译器状态
    /// </summary>
    public void Clean()
    {
        CmdList.Clear();
        CmdList = [];
    }

    /// <summary>
    /// 根据命令管理器信息反编译出Fly命令文本
    /// </summary>
    public string Decompile(List<FlyCmdItem> items)
    {
        var sb = new StringBuilder();
        for (var i = 0; i < items.Count; i++)
        {
            var cmd = items[i];
            var tokenizer = TokenAdmin?.Get(cmd.Id);
            if (tokenizer == null) continue;
            tokenizer.Reverse(cmd, sb);
            cmd.LineNum = GetLineNum(sb) - 1;
        }

        return sb.ToString();
    }

    /// <summary>
    /// 获得当前源码总行数，用于反编译时指令定位
    /// </summary>
    private static int GetLineNum(StringBuilder sb)
    {
        return Regex.Matches(sb.ToString(), Environment.NewLine).Count;
    }

    /// <summary>
    /// 词法分析，将Fly文件解析出词元
    /// </summary>
    private bool LexAnalyze(StreamReader flySr)
    {
        _lineNum = -1;// 使得行号从0开始
        if (null == flySr) return false;
        while (flySr.EndOfStream == false)
        {
            var lineTxt = flySr.ReadLine();
            _lineNum++;
            if (string.IsNullOrEmpty(lineTxt)) continue;
            lineTxt = lineTxt.Trim(); // 逐行处理
            if (string.IsNullOrEmpty(lineTxt)) continue;
            if (lineTxt.StartsWith(@"//") || lineTxt.StartsWith('#'))
            {   // 过滤注释字段
                continue;
            }

            if (false == LinelexAnalyze(lineTxt))
            {
                return false;
            }
        }
        return true;
    }

    /// <summary>
    /// 按行进行词法分析处理，获取词元
    /// </summary>
    private bool LinelexAnalyze(string strData)
    {
        // 首先进行 行尾注释处理, 以 ' (0x27)或 //(0x2F2F) 或 #(0x23) 作为结束符
        var index = strData.IndexOf('\'');
        if (-1 == index) index = strData.IndexOf('#');
        if (-1 == index) index = strData.IndexOf("//", StringComparison.Ordinal);
        if (-1 != index) strData = strData[..index];

        _sb2Linelex.Length = 0;
        // 需要整词元匹配的前字符
        var preMatch = '\0';
        foreach (var ch in strData)
        {
            string strToken;
            if (ch is '[' or '(')
            {
                preMatch = ch;
                strToken = _sb2Linelex.ToString().Trim();
                if (strToken.Length == 0)
                {
                    _sb2Linelex.Append(ch);
                    continue;
                }
                AddToken(new Token(strToken, _lineNum));
                _sb2Linelex.Length = 0;
                _sb2Linelex.Append(ch);
                continue;
            }

            _sb2Linelex.Append(ch);
            if (preMatch == '[' && ch == ']' || preMatch == '(' && ch == ')')
            {   // 匹配到词元[XXX]或(XXX)
                AddToken(new Token(_sb2Linelex.ToString(), _lineNum));
                preMatch = '\0';
                _sb2Linelex.Length = 0;
                continue;
            }

            // 处理词元结束情况之一：以下述分隔符为结尾
            if (preMatch == '\0' && (ch == ' ' || ch == '\t' || ch == ','))
            {
                _sb2Linelex.Remove(_sb2Linelex.Length - 1, 1);// 去除分割符号
                strToken = _sb2Linelex.ToString().Trim();
                if (strToken.Length == 0)
                {
                    continue;
                }
                AddToken(new Token(strToken, _lineNum));
                _sb2Linelex.Length = 0;
            }

            // 处理词元结束情况之二：以关键词为结尾，Fixme此处实现比较丑陋，待改善
            if (preMatch == '\0' && ch == '=')
            {
                strToken = _sb2Linelex.ToString().Trim();
                if (strToken.Length == 0)
                {
                    continue;
                }
                AddToken(new Token(strToken, _lineNum));
                _sb2Linelex.Length = 0;
            }
        }

        // 判断匹配情况
        if (preMatch != '\0')
        {
            var errMsg = $"Find Invalid Token:{_sb2Linelex}";
            Report(errMsg, CmpEventArgs.MsgType.Error, 1 + _lineNum);
            return false;
        }

        if (_sb2Linelex.ToString().Length != 0)
        {
            AddToken(new Token(_sb2Linelex.ToString(), _lineNum));
        }
        return true;
    }

    /// <summary>
    /// 语义分析
    /// </summary>
    private bool SyntaxAnalyze()
    {
        _tokenIndex = 0;

        while (CurToken != null)
        {
            var token = CurToken;
            var tokenizer = TokenAdmin?.Get(token.Name);
            if (tokenizer == null)
            {
                var errMsg = $"Can not analyze Token:{token.Name}";
                Report(errMsg, CmpEventArgs.MsgType.Error, 1 + token.Row);
                return false;
            }

            if (!tokenizer.Process() && !string.IsNullOrEmpty(ErrorMsg))
            {
                Report(ErrorMsg, CmpEventArgs.MsgType.Error, 1 + token.Row);
                return false;
            }
        }
        return true;
    }

    /// <summary>
    /// 根据命令管理器内容，以及LinkInfo信息，生成代码
    /// </summary>
    private bool CodeGen()
    {
        if (CmdList.Count == 0)
        {
            Report("指令为空!", CmpEventArgs.MsgType.Error, -1);
            return false;
        }

        if (CmdList[0].Id != (int)FlyCmdId.CMD_TAKEOFF)
        {
            Report("无起飞指令!", CmpEventArgs.MsgType.Error, -1);
            return false;
        }

        if (!FlyUtils.IsEndCmd(CmdList[^1].Id))
        {
            Report("无结束指令!", CmpEventArgs.MsgType.Error, -1);
            return false;
        }

        // 打印统计信息
        Report($"{Path.GetFileName(_flyFile)} 编译成功!", CmpEventArgs.MsgType.Info, -1);
        return true;
    }

    #region 编译事件报告
    internal void Report(string msg, CmpEventArgs.MsgType msgType, int row)
    {
        ReportMsg = $"L-{row}:{msg}:{msgType}";
        if (msgType == CmpEventArgs.MsgType.Error)
        {
            ErrorMsg = ReportMsg;
        }
        CmpEvent?.Invoke(new CmpEventArgs(msg, msgType, _flyFile, row));
        Trace.WriteLine(ReportMsg);
    }

    internal void Report(string msg, CmpEventArgs.MsgType msgType)
    {
        var row = CurToken?.Row ?? -1;
        ReportMsg = $"L-{row}:{msg}:{msgType}";
        if (msgType == CmpEventArgs.MsgType.Error)
        {
            ErrorMsg = ReportMsg;
        }
        CmpEvent?.Invoke(new CmpEventArgs(msg, msgType, _flyFile, row));
        Trace.WriteLine(ReportMsg);
    }

    #endregion

    /// <summary>
    /// 获取词元流中当前的词元对象
    /// </summary>
    internal Token? CurToken => _tokenIndex < _tokenList.Count ? _tokenList[_tokenIndex] : null;

    /// <summary>
    /// 获取当前行的所有Tokens
    /// </summary>
    /// <returns>具有相同行号的词元</returns>
    internal List<Token>? GetLineTokens()
    {
        if (null == CurToken) return null;
        var row = CurToken.Row;
        var startIndex = _tokenIndex;
        while (null != CurToken && row == CurToken.Row)
        {
            _tokenIndex++;
        }
        var cnt = _tokenIndex - startIndex;
        if (0 == cnt) return null;

        var tokens = new List<Token>();
        for (var i = 0; i < cnt; i++)
        {
            tokens.Add(_tokenList[i + startIndex]);
        }
        return tokens;
    }

    /// <summary>
    /// 增加词元
    /// </summary>
    private void AddToken(Token token)
    {
        // 此处词元添加进行中值操作处理
        if (token.Name.Equals("=") && _tokenList.Count > 0)
        {
            _tokenList.Insert(_tokenList.Count - 1, token);
        }
        else
        {
            _tokenList.Add(token);
        }
    }
}
